package com.cctc.rds.raft.core.log.impl;

import com.cctc.rds.raft.core.log.Log;
import com.cctc.rds.raft.core.log.LogService;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 日志条目 [操作接口] 实现类。
 * - 默认以单向链表的方式存放到内存中，仅供测试使用。
 */
public class DefaultLogService implements LogService {
    private static class Node {
        private Log log;
        private Node prev;

        public Node(Log log) {
            this.log = log;
        }
    }

    private Node tail;
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    @Override
    public Log peek() {
        lock.readLock().lock();
        try {
            return tail != null ? tail.log : null;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    public Log index(long index) {
        lock.readLock().lock();
        try {
            Node cur = tail;
            while (cur != null && cur.log.getIndex() != index) {
                cur = cur.prev;
            }
            return cur != null ? cur.log : null;
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    public Log[] range(long startIndex, long endIndex) {
        lock.readLock().lock();
        try {
            Node cur = tail, end = null;
            int size = 0;
            while (cur != null) {
                size++;
                cur = cur.prev;
            }
            if (endIndex >= size) {
                end = tail;
                endIndex = size;
            } else {
                while ((cur = tail) != null && cur.log.getIndex() != endIndex) {
                    end = cur;
                    cur = cur.prev;
                }
            }
            Log[] ans = new Log[(int) (endIndex - startIndex + 1)];
            int index = ans.length - 1;
            while ((cur = end) != null && cur.log.getIndex() != startIndex) {
                ans[index] = cur.log;
                cur = cur.prev;
                index--;
            }
            if (cur != null) ans[index] = cur.log;
            return ans;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    public void append(Log log) {
        lock.writeLock().lock();
        try {
            Node cur = new Node(log);
            cur.prev = tail;
            tail = cur;
        } finally {
            clear(10000);
            lock.writeLock().unlock();
        }
    }

    private void clear(int before) {
        Node cur = tail, next = null;
        while (before >= 0 && cur != null) {
            next = cur;
            cur = cur.prev;
            before--;
        }
        if (cur != null) next.prev = null;
    }

    @Override
    public void update(Log log) {
        lock.writeLock().lock();
        try {
            Node cur = tail;
            while (cur != null && cur.log.getIndex() != log.getIndex()) {
                cur = cur.prev;
            }
            if (cur != null) cur.log = log;
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    public void remove(long startIndex, long endIndex) {
        lock.writeLock().lock();
        try {
            Node cur = tail, prev = null, next = null;
            int size = 0;
            while (cur != null) {
                size++;
                cur = cur.prev;
            }
            while ((cur = tail) != null && cur.log.getIndex() != endIndex) {
                next = cur;
                cur = cur.prev;
            }
            while ((cur = tail) != null && cur.log.getIndex() != startIndex) {
                cur = cur.prev;
                prev = cur;
            }
            if (endIndex >= size) tail = prev;
            else next.prev = prev;
        } finally {
            lock.writeLock().unlock();
        }
    }
}
